﻿namespace ScrumLite.WebUx.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Web.Mvc;
    using GoogleChartSharp;
    using ScrumLite.Services;
    using ScrumLite.Services.Contracts;
    using ScrumLite.WebUx.Models;
    using ScrumLite.Gateways;
    using System.Globalization;
    using System.Configuration;

    public class CommitmentsController : ContextBaseController
    {
        ICommitmentsService commitments;

        public CommitmentsController()
            : this(new CommitmentsServiceClient(), new ProjectsServiceClient(), new ProfileServiceClient())
        {
        }

        public CommitmentsController(ICommitmentsService commitments, IProjectsService projects, IProfileService profile)
            : base(profile, projects)
        {
            this.commitments = commitments;
        }

        public void StandUps(string projectId)
        {
            List<DateTime> dates = commitments.GetStandupDates(projectId).ToList<DateTime>();

            dates.Remove(DateTime.Now.Date);

            StandupListViewData data = new StandupListViewData(new Collection<DateTime>(dates))
            {
                ProjectName = Projects.GetProjectName(projectId),
                ProjectId = projectId,
                ViewTitle = "StandUps"
            };

            RenderView("Standups", data);
        }

        public void Read(string projectId, DateTime date)
        {
            CommitmentsReadViewData data = GetCommitmentsViewData(projectId, date);
            data.ProjectName = Projects.GetProjectName(projectId);
            data.ViewTitle = "Add or Edit Commitments";
            RenderView("Read", data);
        }

        public void Save(string projectId, string id, string title, string createdBy, string verification, DateTime date, Decimal spent, Decimal left)
        {
            Commitment commitment = new Commitment()
            {
                Title = title,
                CreatedBy = createdBy,
                Verification = verification,
                CreatedOn = date,
                Spent = spent,
                Left = left
            };

            if (String.IsNullOrEmpty(id))
            {
                commitments.Add(projectId, commitment);
            }
            else
            {
                commitment.Id = new Guid(id);
                commitments.Update(commitment);
            }

            CommitmentsReadViewData data = GetCommitmentsViewData(projectId, date);

            this.RenderPartial("Standup", data);
        }

        public void Delete(string projectId, string id, DateTime date)
        {           
            commitments.Delete(new Guid(id));
            
            CommitmentsReadViewData data = GetCommitmentsViewData(projectId, date);

            this.RenderPartial("Standup", data);
        }

        public void CommitmentsMetrics(string projectId)
        {
            Dictionary<int, int> weeklyOpenCommitmentsCount = commitments.GetWeeklyCommitmentsCount(projectId, false);
            Dictionary<int, int> weeklyClosedCommitmentsCount = commitments.GetWeeklyCommitmentsCount(projectId, true);

            CommitmentsMetricsData data = new CommitmentsMetricsData(weeklyOpenCommitmentsCount, weeklyClosedCommitmentsCount)
            {
                ViewTitle = "Commitments Metrics"
            };
            data.GChartWeeklyCommitmentsBars =CreateWeeklyCommitmentsBarsUrl(data);
            data.GChartTotalCommitmentsBars = CreateTotalCommitmentsBarsUrl(data);
            data.ProjectId = projectId;
            data.ProjectName = Projects.GetProjectName(projectId);
            RenderView("CommitmentsMetrics", data);
        }

        #region Google Charts helper methods
        private static string CreateTotalCommitmentsBarsUrl(CommitmentsMetricsData data)
        {
            if (data.WeeklyOpenCommitmentsCount.Count() == 0 && data.WeeklyClosedCommitmentsCount.Count() == 0)
            {
                return String.Format(CultureInfo.InvariantCulture, "/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyCommitmentsBars.png");
            }
            else
            {
                BarChart chartBuilder = new BarChart(400, 350, BarChartOrientation.Horizontal, BarChartStyle.Stacked);
                chartBuilder.SetTitle("Project commitments");
                chartBuilder.SetLegend(new string[] { "Open", "Closed" });
                chartBuilder.SetDataSetColors(new string[] { "ff0000", "00aa00" });

                float totalOpenCommitments = data.WeeklyOpenCommitmentsCount.Sum(w => w.Value);
                float totalClosedCommitments = data.WeeklyClosedCommitmentsCount.Sum(w => w.Value);

                int maxValueLabel = (Convert.ToInt32((totalOpenCommitments + totalClosedCommitments) / 10) + 1) * 10;
                ChartAxis unitAxis = new ChartAxis(ChartAxisType.Bottom);
                unitAxis.SetRange(0, maxValueLabel);
                chartBuilder.AddAxis(unitAxis);

                ChartAxis weeklyAxis = new ChartAxis(ChartAxisType.Left, new string[] { "Total" });
                chartBuilder.AddAxis(weeklyAxis);

                List<float[]> chartData = new List<float[]>();
                chartData.Add(new float[] { totalOpenCommitments });
                chartData.Add(new float[] { totalClosedCommitments });
                chartBuilder.SetData(chartData, maxValueLabel);

                return chartBuilder.GetUrl();
            }
        }

        private static string CreateWeeklyCommitmentsBarsUrl(CommitmentsMetricsData data)
        {
            if (data.WeeklyOpenCommitmentsCount.Count() == 0 && data.WeeklyClosedCommitmentsCount.Count() == 0)
            {
                return String.Format(CultureInfo.InvariantCulture, "/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyCommitmentsBars.png");
            }
            else
            {
                BarChart chartBuilder = new BarChart(400, 350, BarChartOrientation.Horizontal, BarChartStyle.Stacked);
                chartBuilder.SetTitle("Weekly commitments");
                chartBuilder.SetLegend(new string[] { "Open", "Closed" });
                chartBuilder.SetDataSetColors(new string[] { "ff0000", "00aa00" });

                List<int> keys = new List<int>();
                keys.AddRange(data.WeeklyClosedCommitmentsCount.Keys);
                keys = new List<int>(keys.Union(data.WeeklyOpenCommitmentsCount.Keys));

                List<float> openData = new List<float>();
                List<float> closedData = new List<float>();
                float maxValueRange = 0;
                List<string> weekLabels = new List<string>();
                foreach (int weekNumber in keys)
                {
                    openData.Add((data.WeeklyOpenCommitmentsCount.ContainsKey(weekNumber)) ? data.WeeklyOpenCommitmentsCount[weekNumber] : 0);
                    closedData.Add((data.WeeklyClosedCommitmentsCount.ContainsKey(weekNumber)) ? data.WeeklyClosedCommitmentsCount[weekNumber] : 0);
                    maxValueRange = Math.Max(maxValueRange, Math.Max(openData[openData.Count - 1], closedData[closedData.Count - 1]));
                    weekLabels.Add(string.Format(CultureInfo.InvariantCulture, "Week+{0}", weekNumber));
                }

                int maxValueLabel = (Convert.ToInt32(maxValueRange / 10) + 1) * 10;
                ChartAxis unitAxis = new ChartAxis(ChartAxisType.Bottom);
                unitAxis.SetRange(0, maxValueLabel);
                chartBuilder.AddAxis(unitAxis);

                ChartAxis weeklyAxis;
                if (weekLabels.Count > 0)
                {
                    List<string> invertedWeekLabels = new List<string>();
                    for (int index = weekLabels.Count - 1; index >= 0; index--)
                    {
                        invertedWeekLabels.Add(weekLabels[index]);
                    }
                    weeklyAxis = new ChartAxis(ChartAxisType.Left, invertedWeekLabels.ToArray());
                }
                else
                {
                    weeklyAxis = new ChartAxis(ChartAxisType.Left, new string[] { "." });
                }
                chartBuilder.AddAxis(weeklyAxis);

                List<float[]> chartData = new List<float[]>();
                chartData.Add(openData.ToArray());
                chartData.Add(closedData.ToArray());
                chartBuilder.SetData(chartData, maxValueLabel);

                return chartBuilder.GetUrl();
            }
        }
        #endregion

        private CommitmentsReadViewData GetCommitmentsViewData(string projectId, DateTime date)
        {
            Collection<Commitment> todayCommitments = new Collection<Commitment>( commitments.GetCommitments(date, projectId).ToList() );
            
            Collection<Commitment> completedCommitments = GetCompletedCommitments(todayCommitments);
            Collection<Commitment> uncompletedCommitments = GetUncompletedCommitments(todayCommitments);
            CommitmentsReadViewData data = new CommitmentsReadViewData(completedCommitments, uncompletedCommitments)
            {
                ProjectId = projectId,
                Date = date
            };
            return data;
        }

        [NonAction]
        public static Collection<Commitment> GetCompletedCommitments(Collection<Commitment> commitments)
        {
            List<Commitment> result = (from Commitment commitment in commitments
                                       where commitment.Left == 0
                                       orderby commitment.CreatedBy
                                       select commitment).ToList();
            return new Collection<Commitment>(result);
        }

        [NonAction]
        public static Collection<Commitment> GetUncompletedCommitments(Collection<Commitment> commitments)
        {
            List<Commitment> result = (from Commitment commitment in commitments
                                       where commitment.Left > 0
                                       orderby commitment.CreatedBy
                                       select commitment).ToList();
            return new Collection<Commitment>(result);
        }
    }
}
